package njust.csc.aeapi.controller

import njust.csc.aeapi.config.ApplicationVariables
import njust.csc.aeapi.model.MessageModel
import njust.csc.aeapi.model.ResultMessageModel
import njust.csc.aeapi.service.*
import njust.csc.aeapi.utils.LogRequest
import njust.csc.aeapi.utils.getAnalysisNo
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
import java.lang.Thread.sleep
import kotlin.io.path.Path
import kotlin.io.path.extension
import kotlin.io.path.name

/**
 * @Author ZhouHang
 * @Date 24/6/10 周一
 */
@RestController
@RequestMapping("/api")
class BaseController @Autowired constructor(
    private val applicationVariables: ApplicationVariables,
    private val qrCodeService: QRCodeService,
    private val fileService: FileService,
    private val httpService: HttpService,
    private val analysisService: AnalysisService,
) {
    /**
     * 上传 APK 文件并进行分析
     * @param apkFile MultipartFile APK文件
     * @return MessageModel
     */
    @LogRequest
    @PostMapping("/upload_apk")
    fun uploadAPK(
        @RequestParam(value = "file") apkFile: MultipartFile,
    ): MessageModel {
        if (apkFile.isEmpty || !(apkFile.originalFilename ?: "").endsWith(".apk")) {
            return MessageModel(
                code = 3000,
                message = "请上传 APK 文件"
            )
        }

        // 保存 apk 到指定目录
        val apk = fileService.saveToUserInput(apkFile)

        // 生成文件对应的唯一的 md5 编码
        val analysisNo = apkFile.getAnalysisNo()

        // 开始分析 这里 apk.name 是指 apk 文件原名
        analysisService.analysis(apk.name, analysisNo)

        return MessageModel(
            code = 2000,
            message = analysisNo
        )
    }

    /**
     * 上传 URL 并下载 URL 对应 APK 文件，随后进行分析
     * @param apkUrl String apk 下载链接
     * @return MessageModel
     */
    @LogRequest
    @PostMapping("/upload_url")
    fun uploadUrl(
        @RequestParam(value = "url") apkUrl: String,
    ): MessageModel {
        val savePath = httpService.downloadFileToUserInput(apkUrl) ?: return MessageModel(
            code = 3000,
            message = "链接无效或不存在"
        )
        if (savePath.extension != "apk") {
            return MessageModel(
                code = 3001,
                message = "请上传正确的 apk 下载链接"
            )
        }

        // 生成 apk 文件对应的唯一的 md5 编码
        val analysisNo = savePath.getAnalysisNo()

        // 开始静态/动态分析 这里 apk.name 是指 apk 文件原名
        analysisService.analysis(savePath.name, analysisNo)

        return MessageModel(
            code = 2000,
            message = analysisNo
        )
    }

    /**
     * 上传二维码图片文件
     * @param qrcode MultipartFile 二维码图片文件
     * @return MessageModel
     */
    @LogRequest
    @PostMapping("/upload_qrcode")
    fun uploadQrCode(
        @RequestParam(value = "file") qrcode: MultipartFile,
    ): MessageModel {
        // 保存二维码图片
        val qrcodeFile = fileService.saveToUserInput(qrcode)
        val result = qrCodeService.qrcodeDecode(qrcodeFile)
        return uploadUrl(result)
    }

    /**
     * 将 data 编码为二维码后保存到 name 指定的路径
     * @param data String 待生成二维码的的数据
     * @param name String 二维码图片保存路径
     * @return MessageModel
     */
    @LogRequest
    @PostMapping("/generate_qrcode")
    fun generateQrcode(
        @RequestParam(value = "data") data: String,
        @RequestParam(value = "name") name: String,
    ): MessageModel {
        val genDir = Path(applicationVariables.userInputDirPath).resolve(name)
        qrCodeService.qrcodeGenerate(data, genDir)

        return MessageModel(
            code = 2000,
            message = "Success"
        )
    }

    /**
     * 通过分析号获取分析结果
     * @param analysisNo String 分析号
     * @return ResultMessageModel
     */
    @PostMapping("/get_result")
    fun getResult(
        @RequestParam(value = "analysis_no") analysisNo: String,
    ): ResultMessageModel {
        val order = analysisService.getAnalysisOrder(analysisNo)
            ?: return ResultMessageModel(
                code = 3000,
                message = "请输入正确的分析号"
            )
        return ResultMessageModel(
            code = 2000,
            staticStatus = order.staticStatus,
            dynamicStatus = order.dynamicStatus,
            judgeStatus = order.judgeStatus,
            message = analysisService.getAPK(analysisNo)
        )
    }

    /**
     * 通过分析号获取研判结果
     * @param analysisNo String 分析号
     * @return MessageModel
     */
    @LogRequest
    @PostMapping("/get_judge_result")
    fun getJudgeResult(
        @RequestParam(value = "analysis_no") analysisNo: String,
    ): MessageModel {
        val order = analysisService.getAnalysisOrder(analysisNo)
            ?: return MessageModel(
                code = 3000,
                message = "请输入正确的分析号"
            )

        return when (order.judgeStatus) {
            "Success" -> MessageModel(
                code = 2000,
                message = analysisService.getJudgeResult(order)
            )
            "Judging" -> MessageModel(
                code = 4000,
                message = "正在研判中，请稍后"
            )
            else -> MessageModel(
                code = 5000,
                message = "分析失败，原因：${order.judgeStatus}"
            )
        }
    }

    /**
     * 根据分析号获取 APP 动态运行图像
     * @param analysisNo String
     * @return MessageModel
     */
    @LogRequest
    @PostMapping("/get_screencaps")
    fun getScreencaps(
        @RequestParam(value = "analysis_no") analysisNo: String,
    ): MessageModel {
        return MessageModel(
            code = 2000,
            message = analysisService.getScreencaps(analysisNo)
        )
    }

    /**
     * 获取黑白名单（即所有已分析的APK列表）
     * @return MessageModel
     */
    @LogRequest
    @GetMapping("/get_black_list")
    fun getBlackList(): MessageModel {
        return MessageModel(
            code = 2000,
            message = analysisService.getBlackList()
        )
    }

    /**
     * 删除对应分析号的数据（数据库记录+分析结果）
     * @param analysisNo String 分析号
     * @return MessageModel
     */
    @LogRequest
    @PostMapping("/delete_analysis")
    fun deleteAnalysis(
        @RequestParam(value = "analysis_no") analysisNo: String,
    ): MessageModel {
        analysisService.deleteAnalysis(analysisNo)
        return MessageModel(
            code = 2000,
            message = "Success"
        )
    }

    /**
     * 获取一个 apk 文件，但什么也不做，用于兼容前端
     * @param apkFile MultipartFile apk 文件
     * @return MessageModel
     */
    @PostMapping("/throwaway")
    fun throwaway(
        @RequestParam(value = "file") apkFile: MultipartFile,
    ): MessageModel {
        return MessageModel(
            code = 2000,
            message = ""
        )
    }

    // MARK: Test Controllers
    @LogRequest
    @GetMapping("/hello_get")
    fun helloGet(): String {
        return "Hello, APK!"
    }

    @LogRequest
    @PostMapping("/hello_post")
    fun helloPost(
        @RequestParam("name") name: String,
    ): String {
        return "Hello, $name"
    }

    @PostMapping("/test")
    fun test(
        @RequestParam(value = "n") n: Int,
    ): Int {
        println(n)
        while (true) {
            sleep(1000)
            print(n)
        }
    }
}
